import ctypes
import threading
import time
import win32gui
import win32con
import win32api
import win32process
import os
import tkinter as tk
from tkinter import ttk, messagebox, scrolledtext, simpledialog
import webbrowser
import psutil

# 默认设置
DEFAULT_HOST_WINDOW_NAME = "宿主窗口"
DEFAULT_TARGET_WINDOW_CLASS = "DIBFullViewClass"

# 全局变量
class_name = "HostWindowClass"
top_most = True
g_rect_window_before_fullscreen = (0, 0, 0, 0)
g_b_fullscreen = False
g_got_target = False
g_debug = False
h_target = 0
h_host = 0
target_process_name = ""
previous_focus_window = 0


class Application(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("窗口控制突破工具")
        self.geometry("900x650")
        self.protocol("WM_DELETE_WINDOW", self.on_close)

        # 设置样式
        self.style = ttk.Style()
        self.style.configure('TFrame', background='#f0f0f0')
        self.style.configure('TLabel', background='#f0f0f0', font=('微软雅黑', 9))
        self.style.configure('TButton', font=('微软雅黑', 9))
        self.style.configure('TEntry', font=('微软雅黑', 9))
        self.style.configure('TCombobox', font=('微软雅黑', 9))
        self.style.configure('TCheckbutton', background='#f0f0f0', font=('微软雅黑', 9))

        self.create_widgets()

        self.console_thread = None
        self.defender_thread = None
        self.find_thread = None
        self.start_threads()

    def start_threads(self):
        """启动所有后台线程"""
        # 控制台输入线程
        if not self.console_thread or not self.console_thread.is_alive():
            self.console_thread = threading.Thread(target=self.con_in, daemon=True)
            self.console_thread.start()

        # 键盘防御线程
        if not self.defender_thread or not self.defender_thread.is_alive():
            self.defender_thread = threading.Thread(target=keyboard_defender, daemon=True)
            self.defender_thread.start()

        # 窗口查找线程
        if not self.find_thread or not self.find_thread.is_alive():
            self.find_thread = threading.Thread(target=self.find_wd, daemon=True)
            self.find_thread.start()

        self.log_message("所有后台线程已启动")

    def create_widgets(self):
        # 主容器
        main_frame = ttk.Frame(self)
        main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

        # 左侧面板 - 控制区
        control_frame = ttk.LabelFrame(main_frame, text="控制面板", padding=(10, 5))
        control_frame.pack(side=tk.LEFT, fill=tk.Y, padx=(0, 10))

        # 捕获方式选择
        capture_method_frame = ttk.LabelFrame(control_frame, text="捕获方式", padding=(10, 5))
        capture_method_frame.pack(fill=tk.X, pady=(0, 10))

        self.capture_method = tk.StringVar(value="class")
        ttk.Radiobutton(capture_method_frame, text="窗口类名", variable=self.capture_method,
                        value="class", command=self.update_capture_method).pack(anchor=tk.W)
        ttk.Radiobutton(capture_method_frame, text="进程名称", variable=self.capture_method,
                        value="process", command=self.update_capture_method).pack(anchor=tk.W)
        ttk.Radiobutton(capture_method_frame, text="前一焦点窗口", variable=self.capture_method,
                        value="previous", command=self.update_capture_method).pack(anchor=tk.W)

        # 设置框架
        settings_frame = ttk.Frame(control_frame)
        settings_frame.pack(fill=tk.X, pady=(0, 10))

        # 窗口类名设置
        self.class_frame = ttk.Frame(settings_frame)
        ttk.Label(self.class_frame, text="目标窗口类名:").pack(anchor=tk.W)
        self.target_class_entry = ttk.Entry(self.class_frame)
        self.target_class_entry.insert(0, DEFAULT_TARGET_WINDOW_CLASS)
        self.target_class_entry.pack(fill=tk.X, pady=(0, 5))

        # 进程名设置
        self.process_frame = ttk.Frame(settings_frame)
        ttk.Label(self.process_frame, text="目标进程名称:").pack(anchor=tk.W)
        self.process_name_entry = ttk.Entry(self.process_frame)
        self.process_name_entry.pack(fill=tk.X, pady=(0, 5))
        ttk.Button(self.process_frame, text="获取进程列表", command=self.show_process_list).pack(fill=tk.X, pady=(0, 5))

        # 前一焦点窗口设置
        self.previous_frame = ttk.Frame(settings_frame)
        ttk.Label(self.previous_frame, text="将自动捕获切换到此工具前的焦点窗口").pack(anchor=tk.W)
        ttk.Button(self.previous_frame, text="获取前一焦点窗口", command=self.get_previous_window).pack(fill=tk.X, pady=(0, 5))

        self.class_frame.pack(fill=tk.X)

        ttk.Label(settings_frame, text="宿主窗口标题:").pack(anchor=tk.W)
        self.host_title_entry = ttk.Entry(settings_frame)
        self.host_title_entry.insert(0, DEFAULT_HOST_WINDOW_NAME)
        self.host_title_entry.pack(fill=tk.X, pady=(0, 10))

        # 选项框架
        options_frame = ttk.Frame(control_frame)
        options_frame.pack(fill=tk.X, pady=(0, 10))

        self.topmost_var = tk.BooleanVar(value=top_most)
        self.debug_var = tk.BooleanVar(value=g_debug)

        ttk.Checkbutton(options_frame, text="窗口置顶", variable=self.topmost_var,
                        command=self.toggle_topmost).pack(anchor=tk.W)
        ttk.Checkbutton(options_frame, text="调试模式", variable=self.debug_var,
                        command=self.toggle_debug).pack(anchor=tk.W)

        # 按钮框架
        buttons_frame = ttk.Frame(control_frame)
        buttons_frame.pack(fill=tk.X, pady=(10, 0))

        ttk.Button(buttons_frame, text="开始捕获", command=self.start_embedding).pack(fill=tk.X, pady=2)
        ttk.Button(buttons_frame, text="停止捕获", command=self.stop_embedding).pack(fill=tk.X, pady=2)
        ttk.Button(buttons_frame, text="刷新窗口", command=self.refresh_windows).pack(fill=tk.X, pady=2)
        ttk.Button(buttons_frame, text="项目网站", command=self.open_website).pack(fill=tk.X, pady=2)

        # 右侧面板 - 日志输出
        log_frame = ttk.LabelFrame(main_frame, text="日志输出", padding=(10, 5))
        log_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)

        self.log_text = scrolledtext.ScrolledText(log_frame, wrap=tk.WORD, font=('Consolas', 9))
        self.log_text.pack(fill=tk.BOTH, expand=True)

        # 状态栏
        self.status_var = tk.StringVar()
        self.status_var.set("就绪")
        status_bar = ttk.Label(main_frame, textvariable=self.status_var, relief=tk.SUNKEN)
        status_bar.pack(side=tk.BOTTOM, fill=tk.X, pady=(10, 0))

    def update_capture_method(self):
        # 根据选择的捕获方式显示对应的设置控件
        method = self.capture_method.get()
        self.class_frame.pack_forget()
        self.process_frame.pack_forget()
        self.previous_frame.pack_forget()

        if method == "class":
            self.class_frame.pack(fill=tk.X)
        elif method == "process":
            self.process_frame.pack(fill=tk.X)
        elif method == "previous":
            self.previous_frame.pack(fill=tk.X)

    def log_message(self, message):
        self.log_text.insert(tk.END, f"{message}\n")
        self.log_text.see(tk.END)
        self.update_idletasks()

    def update_status(self, message):
        self.status_var.set(message)
        self.log_message(f"状态: {message}")

    def toggle_topmost(self):
        global top_most
        top_most = self.topmost_var.get()
        self.log_message(f"窗口置顶 {'已启用' if top_most else '已禁用'}")

    def toggle_debug(self):
        global g_debug
        g_debug = self.debug_var.get()
        self.log_message(f"调试模式 {'已启用' if g_debug else '已禁用'}")

    def start_embedding(self):
        global host_window_name, target_window_class, target_process_name, previous_focus_window

        host_window_name = self.host_title_entry.get()
        method = self.capture_method.get()

        if method == "class":
            target_window_class = self.target_class_entry.get()
            self.update_status(f"开始捕获 - 目标窗口类: {target_window_class}")
        elif method == "process":
            target_process_name = self.process_name_entry.get()
            if not target_process_name:
                messagebox.showerror("错误", "请输入进程名称")
                return
            self.update_status(f"开始捕获 - 目标进程: {target_process_name}")
        elif method == "previous":
            if not hasattr(self, 'target_window_info'):
                messagebox.showerror("错误", "请先获取并确认目标窗口")
                return
            previous_focus_window = self.target_window_info['handle']
            self.update_status(f"开始捕获窗口: {self.target_window_info['title']}")

    def stop_embedding(self):
        global g_got_target, h_target, h_host
        if g_got_target and h_target:
            win32gui.ShowWindow(h_host, win32con.SW_HIDE)
            g_got_target = False
            self.update_status("捕获已停止")
        else:
            self.log_message("没有活动的捕获任务可停止")

    def refresh_windows(self):
        global h_target, g_got_target
        h_target = 0
        g_got_target = False
        self.update_status("正在刷新窗口搜索...")

    def open_website(self):
        webbrowser.open("https://gitee.com/vekariswesion/character-world-screen-control-breaker")
        self.log_message("已打开项目网站")

    def show_process_list(self):
        process_list = []
        for proc in psutil.process_iter(['pid', 'name']):
            process_list.append(f"{proc.info['name']} (PID: {proc.info['pid']})")

        process_window = tk.Toplevel(self)
        process_window.title("运行中的进程列表")
        process_window.geometry("400x500")

        listbox = tk.Listbox(process_window, font=('微软雅黑', 10))
        scrollbar = ttk.Scrollbar(process_window, orient="vertical", command=listbox.yview)
        listbox.configure(yscrollcommand=scrollbar.set)

        for proc in sorted(process_list):
            listbox.insert(tk.END, proc)

        def on_select():
            selected = listbox.get(listbox.curselection())
            process_name = selected.split(" (PID:")[0]
            self.process_name_entry.delete(0, tk.END)
            self.process_name_entry.insert(0, process_name)
            process_window.destroy()

        select_button = ttk.Button(process_window, text="选择", command=on_select)

        listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        select_button.pack(pady=5)

    def get_previous_window(self):
        global previous_focus_window
        # 获取当前焦点窗口（工具窗口自身）
        current_focus = win32gui.GetForegroundWindow()

        # 显示更详细的操作指引
        msg = """请按照以下步骤操作：

    1. 保持此提示窗口打开
    2. 切换到您想要捕获的目标窗口
    3. 等待3秒
    4. 然后切换回此工具窗口

    点击确定后开始窗口捕获流程"""
        messagebox.showinfo("操作指引", msg)

        # 设置3秒延迟后开始捕获流程
        self.after(3000, self._capture_previous_window, current_focus)

    def _capture_previous_window(self, current_focus):
        global previous_focus_window
        try:
            # 获取当前活动窗口
            previous_focus_window = win32gui.GetForegroundWindow()

            # 验证窗口有效性
            if (previous_focus_window != current_focus and
                    previous_focus_window != self.winfo_id() and
                    win32gui.IsWindow(previous_focus_window)):

                # 获取窗口详细信息
                _, pid = win32process.GetWindowThreadProcessId(previous_focus_window)
                try:
                    process = psutil.Process(pid)
                    process_name = process.name()
                except:
                    process_name = "未知进程"

                title = win32gui.GetWindowText(previous_focus_window) or "无标题"
                class_name = win32gui.GetClassName(previous_focus_window)

                # 显示窗口信息并询问用户
                info_msg = (
                    f"已找到以下窗口：\n\n"
                    f"窗口标题：{title}\n"
                    f"窗口类名：{class_name}\n"
                    f"进程名称：{process_name}\n\n"
                    f"是否要使用此窗口？\n"
                    f"(确认后请点击'开始捕获'按钮)"
                )

                if messagebox.askyesno("确认窗口", info_msg):
                    self.log_message(f"已记录目标窗口: {title} (类: {class_name}, 进程: {process_name})")
                    self.update_status(f"已记录目标窗口，请点击'开始捕获'")

                    # 保存窗口信息但不立即开始捕获
                    self.target_window_info = {
                        'handle': previous_focus_window,
                        'title': title,
                        'class': class_name,
                        'process': process_name
                    }
                else:
                    previous_focus_window = 0
                    self.log_message("用户取消了窗口选择")
                    self.update_status("等待选择目标窗口")
            else:
                raise Exception("获取的窗口无效")

        except Exception as e:
            error_msg = (
                "窗口捕获失败\n\n"
                "可能原因：\n"
                "1. 未正确切换窗口\n"
                "2. 目标窗口已关闭\n"
                "3. 操作过程超时\n\n"
                "请重试"
            )
            messagebox.showerror("错误", error_msg)
            previous_focus_window = 0
            self.log_message(f"窗口捕获失败: {str(e)}")
            self.update_status("窗口捕获失败，请重试")

    def on_close(self):
        if messagebox.askokcancel("退出", "确定要退出应用程序吗？"):
            self.destroy()
            os._exit(0)

    def con_in(self):
        """替换原来的控制台输入函数"""
        self.log_message("窗口控制突破工具-alpha-0.0.1")
        self.log_message("版权所有 (C) 2025 VekarisWesion")
        self.log_message("准备就绪...")

    def find_wd(self):
        global h_target, h_host, g_got_target, target_window_class, target_process_name, previous_focus_window

        while True:
            method = self.capture_method.get()

            if method == "class":
                target_window_class = self.target_class_entry.get()
                h_target = win32gui.FindWindow(target_window_class, None)
            elif method == "process":
                target_process_name = self.process_name_entry.get()
                h_target = self.find_window_by_process(target_process_name)
            elif method == "previous":
                if previous_focus_window and win32gui.IsWindow(previous_focus_window):
                    h_target = previous_focus_window

            if h_target and not g_got_target:
                self.log_message(f"找到目标窗口: {win32gui.GetWindowText(h_target)}")
                g_got_target = True
                win32gui.ShowWindow(h_host, win32con.SW_SHOW)
                embed_window()
                self.update_status(f"已嵌入窗口: {win32gui.GetWindowText(h_target)}")

            time.sleep(0.25)

    def find_window_by_process(self, process_name):
        def callback(hwnd, hwnds):
            if win32gui.IsWindowVisible(hwnd) and win32gui.GetWindowText(hwnd):
                _, pid = win32process.GetWindowThreadProcessId(hwnd)
                try:
                    if psutil.Process(pid).name().lower() == process_name.lower():
                        hwnds.append(hwnd)
                except:
                    pass
            return True

        hwnds = []
        win32gui.EnumWindows(callback, hwnds)
        return hwnds[0] if hwnds else 0


def host_wnd_proc(hwnd, msg, wparam, lparam):
    global g_b_fullscreen, g_rect_window_before_fullscreen, h_target, h_host, g_got_target

    if msg == win32con.WM_SIZE:
        if h_target and g_got_target:
            win32gui.InvalidateRect(h_target, None, True)
            win32gui.UpdateWindow(h_target)
            rect = win32gui.GetClientRect(hwnd)
            win32gui.SetWindowPos(h_target, 0, 0, 0, rect[2], rect[3],
                                  win32con.SWP_NOZORDER | win32con.SWP_FRAMECHANGED)
        win32gui.SetWindowPos(h_host, 0, 0, 0, 0, 0,
                              win32con.SWP_NOZORDER | win32con.SWP_FRAMECHANGED |
                              win32con.SWP_NOMOVE | win32con.SWP_NOSIZE)
        return 0

    elif msg == win32con.WM_KEYDOWN:
        if wparam == win32con.VK_ESCAPE:
            g_b_fullscreen = not g_b_fullscreen
            if g_b_fullscreen:
                g_rect_window_before_fullscreen = win32gui.GetWindowRect(hwnd)
                win32gui.SetWindowLong(hwnd, win32con.GWL_STYLE, win32con.WS_POPUP | win32con.WS_VISIBLE)
                win32gui.SetWindowPos(hwnd, win32con.HWND_TOP,
                                      0, 0,
                                      win32api.GetSystemMetrics(win32con.SM_CXSCREEN),
                                      win32api.GetSystemMetrics(win32con.SM_CYSCREEN),
                                      win32con.SWP_FRAMECHANGED)
            else:
                win32gui.SetWindowLong(hwnd, win32con.GWL_STYLE, win32con.WS_OVERLAPPEDWINDOW | win32con.WS_VISIBLE)
                win32gui.SetWindowPos(hwnd, 0,
                                      g_rect_window_before_fullscreen[0],
                                      g_rect_window_before_fullscreen[1],
                                      g_rect_window_before_fullscreen[2] - g_rect_window_before_fullscreen[0],
                                      g_rect_window_before_fullscreen[3] - g_rect_window_before_fullscreen[1],
                                      win32con.SWP_FRAMECHANGED | win32con.SWP_NOZORDER)
            return 0

    elif msg == win32con.WM_DESTROY:
        win32gui.PostQuitMessage(0)
        return 0

    elif msg == win32con.WM_KEYDOWN:
        if h_target and g_got_target:
            win32gui.PostMessage(h_target, msg, wparam, lparam)

    return win32gui.DefWindowProc(hwnd, msg, wparam, lparam)


def create_host_window(h_target):
    wc = win32gui.WNDCLASS()
    wc.lpfnWndProc = host_wnd_proc
    wc.hInstance = win32api.GetModuleHandle(None)
    wc.lpszClassName = class_name
    wc.hbrBackground = win32gui.GetStockObject(win32con.WHITE_BRUSH)
    class_atom = win32gui.RegisterClass(wc)

    hwnd = win32gui.CreateWindowEx(
        0, class_name, DEFAULT_HOST_WINDOW_NAME,
        win32con.WS_POPUP | win32con.WS_OVERLAPPEDWINDOW,
        win32con.CW_USEDEFAULT, win32con.CW_USEDEFAULT,
        800, 600, None, None, win32api.GetModuleHandle(None), None
    )

    win32gui.SetWindowLong(hwnd, win32con.GWL_USERDATA, h_target)
    return hwnd


def embed_window():
    global h_target, h_host

    if not h_target or not win32gui.IsWindow(h_target) or not win32gui.IsWindowVisible(h_target) or win32gui.GetParent(
            h_target):
        return

    style = win32gui.GetWindowLong(h_target, win32con.GWL_STYLE)
    ex_style = win32gui.GetWindowLong(h_target, win32con.GWL_EXSTYLE)

    style &= ~win32con.WS_POPUP
    style |= win32con.WS_CHILD
    ex_style &= ~win32con.WS_EX_TOPMOST

    win32gui.SetWindowLong(h_target, win32con.GWL_STYLE, style)
    win32gui.SetWindowLong(h_target, win32con.GWL_EXSTYLE, ex_style)

    win32gui.SetParent(h_target, h_host)

    win32gui.InvalidateRect(h_target, None, True)
    win32gui.UpdateWindow(h_target)
    rect = win32gui.GetClientRect(h_host)
    win32gui.SetWindowPos(h_target, 0, rect[0], rect[1], rect[2], rect[3],
                          win32con.SWP_NOZORDER | win32con.SWP_FRAMECHANGED)
    win32gui.SetWindowPos(h_host, 0, 0, 0, 0, 0,
                          win32con.SWP_NOZORDER | win32con.SWP_FRAMECHANGED |
                          win32con.SWP_NOMOVE | win32con.SWP_NOSIZE)

    ctypes.windll.user32.BlockInput(False)
    win32gui.EnableWindow(h_target, True)


def keyboard_defender():
    while True:
        if h_target:
            ctypes.windll.user32.BlockInput(False)
            win32gui.EnableWindow(h_target, True)
        time.sleep(1)


def main():
    global h_host, h_target

    # 创建宿主窗口
    h_host = create_host_window(h_target)
    win32gui.ShowWindow(h_host, win32con.SW_HIDE)

    # 启动GUI应用
    app = Application()
    app.mainloop()


if __name__ == "__main__":
    main()
